#pragma once
#include <iostream>
#include <mutex>
#include <memory>
#include <unordered_map>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include "log.hpp"

using namespace std;

// session类，里面保存了当前会话中用户的数据以及当前用户的状态,一个用于保持会话的计时器，用户ID，sessionID

typedef websocketpp ::server<websocketpp::config::asio> wsserver_t;

typedef enum
{
	ONLINE,
	OFFLINE
} session_statu;
class session
{
public:
	session(uint64_t ssid) : _ssid(ssid), _sstatu(OFFLINE) { DLOG("%p session 已创建", this); }
	void set_user(uint64_t user) { _uid = user; }
	uint64_t get_user() { return _uid; }
	uint64_t ssid() {return _ssid;}
	void set_statu(session_statu statu) { _sstatu = statu; }
	bool is_login() { return _sstatu == ONLINE; }
	void set_timer(wsserver_t::timer_ptr timer) { _timep = timer; }

	wsserver_t::timer_ptr get_timer() { return _timep; }
	~session() { DLOG("%p session 已析构", this); }

private:
	session_statu _sstatu;
	uint64_t _ssid;
	uint64_t _uid;
	wsserver_t::timer_ptr _timep;
};

#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1
using seesion_ptr = shared_ptr<session>;

class session_manager
{
public:
	session_manager(wsserver_t *server)
		: _next_session(1), _server(server)
	{
		DLOG("%p session【管理器】已创建");
	}

	seesion_ptr create_session(uint64_t ssid, session_statu statu)
	{
		unique_lock<mutex> lock(_mutex);
		seesion_ptr sp(new session(ssid));
		sp->set_statu(statu);
		sp->set_user(ssid);
		_session.insert(make_pair(ssid, sp));
		_next_session++;
		return sp;
	}

	seesion_ptr get_session_by_ssid(uint64_t ssid)
	{
		unique_lock<mutex> lock(_mutex);
		if (_session.find(ssid) == _session.end())
		{
			ELOG("未能根据IP找到对应的会话");
			return seesion_ptr();
		}

		return _session[ssid];
	}

	void remove_session(uint64_t ssid)
	{
		unique_lock<mutex> lock(_mutex);
		_session.erase(ssid);
	}

	void append_session(const seesion_ptr &ssp) {
		std::unique_lock<std::mutex> lock(_mutex);
		_session.insert(std::make_pair(ssp->ssid(), ssp));
	}

	void set_session_expire_time(uint64_t ssid, int ms)
	{
		// 依赖于websocketpp的定时器来完成session⽣命周期的管理。
		//  登录之后，创建session，session需要在指定时间⽆通信后删除
		//  但是进⼊游戏⼤厅，或者游戏房间，这个session就应该永久存在
		//  等到退出游戏⼤厅，或者游戏房间，这个session应该被重新设置为临时，在⻓时间⽆通信后被删除
		seesion_ptr ssp = get_session_by_ssid(ssid);
		if (ssp.get() == nullptr)
		{
			return;
		}
		wsserver_t::timer_ptr tp = ssp->get_timer();
		if (tp.get() == nullptr && ms == SESSION_FOREVER)
		{
			// 1. 在session永久存在的情况下，设置永久存在
			return;
		}
		else if (tp.get() == nullptr && ms != SESSION_FOREVER)
		{
			// 2. 在session永久存在的情况下，设置指定时间之后被删除的定时任务
			wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,std::bind(&session_manager::remove_session, this, ssid));
			ssp->set_timer(tmp_tp);
		}
		else if (tp.get() != nullptr && ms == SESSION_FOREVER)
		{
			// 3. 在session设置了定时删除的情况下，将session设置为永久存在
			// 删除定时任务--- stready_timer删除定时任务会导致任务直接被执⾏
			tp->cancel(); // 因为这个取消定时任务并不是⽴即取消的
			// 因此重新给session管理器中，添加⼀个session信息, 且添加的时候需要使⽤定时器，⽽不是⽴即添加
			
			ssp->set_timer(wsserver_t::timer_ptr()); // 将session关联的定时器设置为空
			
			_server->set_timer(0,bind(&session_manager::append_session, this, ssp));
		}
		else if (tp.get() != nullptr && ms != SESSION_FOREVER)
		{
			// 4. 在session设置了定时删除的情况下，将session重置删除时间。
			tp->cancel(); // 因为这个取消定时任务并不是⽴即取消的
			ssp->set_timer( wsserver_t:: timer_ptr ());
			_server->set_timer(0,bind(&session_manager::append_session, this, ssp));
			// 重新给session添加定时销毁任务
			wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,bind(&session_manager::remove_session, this, ssp -> ssid()));
			// 重新设置session关联的定时器
			ssp->set_timer(tmp_tp);
		}
	}

	~session_manager()
	{
		DLOG("%p session【管理器】已析构");
	}

private:
	mutex _mutex;
	uint64_t _next_session;
	wsserver_t *_server;
	unordered_map<uint64_t, seesion_ptr> _session;
};
